﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace OrderEntryProject
{
   /// <summary>
   /// Interaction logic for MainWindow.xaml
   /// </summary>
   public partial class MainWindow : Window
   {
      private readonly NorthwindDataContext ctx = new NorthwindDataContext();
       private const String MyData = "Hello";
       private const String MyPassword = "OpenSesanne";
       private Byte[] _cypherText;
       private readonly Byte[] _salt = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0};
   		private Byte[] _rsaCipherText;
   	private Byte[] _digitalSignature;


      public MainWindow()
      {
         InitializeComponent();
      	Application.Current.DispatcherUnhandledException += UnhandledException;
      }

      private void Window_Loaded(object sender, RoutedEventArgs e)
      {
		 ctx.Log = new LinqToSqlTraceWriter("DataLogging");
         App.Current.Properties["ctx"] = ctx;
         cmbCustomers.ItemsSource = from c in ctx.Customers
                                    select new Tuple<string,string>
                                       (
                                          c.CustomerID, 
                                          c.CompanyName
                                       );
         cmbCustomers.DisplayMemberPath = "Item2";
      }

      private void mnuSave_Click(object sender, RoutedEventArgs e)
      {
      	var done = false;
		  while(!done)
		  {
		  	try
		  	{
				ctx.SubmitChanges();
				MessageBox.Show("Saved");
				cmbCustomers_SelectionChanged(null, null);
				done = true;
		  	}
		  	catch (SqlException)
		  	{
		  		var result = MessageBox.Show("Error saving changes, retry?", "Save error", MessageBoxButton.OKCancel);
		  		if (result == MessageBoxResult.Cancel)
		  		{
		  			return;
		  		}
		  	}
		  }
      }

      private void mnuOrder_Click(object sender, RoutedEventArgs e)
      {
			var customer = (Tuple<string,string>)cmbCustomers.SelectedValue;
            var window = new OrderWindow {CustomerID = customer == null ? "0" : customer.Item1 };
            window.ShowDialog();
      }

      private void mnuExit_Click(object sender, RoutedEventArgs e)
      {
         Application.Current.Shutdown();
      }

      private void cmbCustomers_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {
         var customer = (Tuple<string,string>)cmbCustomers.SelectedValue;
         if (customer == null) return;
         lstOrders.ItemsSource =
            from o in ctx.Orders
            where o.CustomerID == customer.Item1
            select new
               {
                  o.OrderID,
                  o.OrderDate,
                  o.RequiredDate
               };
      }

	   private void UnhandledException(Object sender, DispatcherUnhandledExceptionEventArgs e)
	   {
	   		var ex = e.Exception;
	   	var message = String.Format("Exception Type:{0}{1}Message:{2}{1}Stack Trace:{3}{1}", ex.GetType().Name,
	   	                            Environment.NewLine, ex.Message, ex.StackTrace);

		   EventLog.WriteEntry(Assembly.GetExecutingAssembly().GetName().Name, message, EventLogEntryType.Error);
	   	MessageBox.Show("This app has encountered a problem and will shutdown. Please contact your administrator.");
		Application.Current.Shutdown();
	   }

       private void mnuSymmetricEncryption_Click(object sender, RoutedEventArgs e)
       {
           var key = new Rfc2898DeriveBytes(MyPassword, _salt);

           //Encrypt the data
           var algo = new RijndaelManaged {Key = key.GetBytes(16), IV = key.GetBytes(16)};

           var sourceBytes = new UnicodeEncoding().GetBytes(MyData);
           
           using(var sourceStream = new MemoryStream(sourceBytes))
            using(var destinationStream = new MemoryStream())
                using(var crypto = new CryptoStream(sourceStream, algo.CreateEncryptor(), CryptoStreamMode.Read))
                {
                    MoveBytes(crypto, destinationStream);
                    _cypherText = destinationStream.ToArray();
                }

           MessageBox.Show(String.Format("Data:{0}{1}Encrypted and encoded:{2}", MyData, Environment.NewLine, Convert.ToBase64String(_cypherText)));
       }

	   #region Helper Methods

	   private void MoveBytes(Stream sourceStream, Stream destinationStream)
       {
           var bytes = new Byte[2048];
           var count = sourceStream.Read(bytes, 0, bytes.Count());

           while(count != 0)
           {
                destinationStream.Write(bytes, 0, count);
               count = sourceStream.Read(bytes, 0, bytes.Length);
           }
       }

	   public String ToHexString(Byte[] bytes)
	   {
	   	var hex = new StringBuilder();
	   	foreach (var b in bytes)
	   	{
	   		hex.AppendFormat("{0:x2}", b);
	   	}
	   	return hex.ToString();
	   }

	   #endregion

	   private void mnuSymmetricDecription_Click(object sender, RoutedEventArgs e)
       {
           if(_cypherText == null)
           {
               MessageBox.Show("First fill ciphertext");
               return;
           }

           var key = new Rfc2898DeriveBytes(MyPassword, _salt);

           var algo = new RijndaelManaged
                          {
                              Key = key.GetBytes(16),
                              IV = key.GetBytes(16)
                          };

           using(var sourceStream = new MemoryStream(_cypherText))
            using(var destinationStream = new MemoryStream())
                using(var cryptoStream = new CryptoStream(sourceStream, algo.CreateDecryptor(), CryptoStreamMode.Read))
                {
                    MoveBytes(cryptoStream, destinationStream);
                    var decryptedBytes = destinationStream.ToArray();
                    var decryptedMessage = new UnicodeEncoding().GetString(decryptedBytes);
                    MessageBox.Show(decryptedMessage);
                }
       }

	   private void mnuAsymmetricEncryption_Click(object sender, RoutedEventArgs e)
	   {
	   		var rsa = 1;

	   		var cspParams = new CspParameters(rsa)
	   		                	{
	   		                		Flags = CspProviderFlags.UseMachineKeyStore,
									KeyContainerName = "My Keys"
	   		                	};
	   		var algo = new RSACryptoServiceProvider(cspParams);

	   	var sourceBytes = new UnicodeEncoding().GetBytes(MyData);

	   	_rsaCipherText = algo.Encrypt(sourceBytes, true);

	   	MessageBox.Show(String.Format("Data: {0}{1}Encrypted and Encoded: {2}", MyData, Environment.NewLine,
	   	                              Convert.ToBase64String(_rsaCipherText)));
	   }

	   private void mnuAsymmetricDecryption_Click(object sender, RoutedEventArgs e)
	   {
			if(_rsaCipherText == null)
			{
				MessageBox.Show("Please populate _rsaCiphertext before decrypting");
				return;
			}

	   	var rsa = 1;
	   	var cspParams = new CspParameters(rsa)
	   	                	{
								Flags = CspProviderFlags.UseMachineKeyStore,
								KeyContainerName = "My Keys"
	   	                	};

	   	var algo = new RSACryptoServiceProvider(cspParams);
	   	var decrypted = algo.Decrypt(_rsaCipherText, true);
	   	MessageBox.Show(new UnicodeEncoding().GetString(decrypted));
	   }

	   private void mnuHash_Click(object sender, RoutedEventArgs e)
	   {
	   	const string data = "Ololo i`m a driver of UFO!";
	   	var bytes = new UnicodeEncoding().GetBytes(data);
	   	var sha = new SHA256Managed();
	   	var hash = sha.ComputeHash(bytes);

	   	MessageBox.Show(ToHexString(hash));
	   }

	   private void mnuDigitalSignature_Click(object sender, RoutedEventArgs e)
	   {
	   	const String data = "Ololo i am a UFO driver!";
	   	var rsa = 24;
	   	var cspParams = new CspParameters(rsa)
	   	                	{
								Flags = CspProviderFlags.UseMachineKeyStore,
								KeyContainerName = "My Keys"
	   	                	};

	   	var algo = new RSACryptoServiceProvider(cspParams);
	   	var sourceBytes = new UnicodeEncoding().GetBytes(data);
	   	_digitalSignature = algo.SignData(sourceBytes, "SHA256");

	   	MessageBox.Show(ToHexString(_digitalSignature));
	   }

	   private void mnuVerifyDigitalSignature_Click(object sender, RoutedEventArgs e)
	   {
		   const String data = "Ololo i am a UFO driver!";
		   var rsa = 24;
		   var cspParams = new CspParameters(rsa)
		   {
			   Flags = CspProviderFlags.UseMachineKeyStore,
			   KeyContainerName = "My Keys"
		   };

		   var algo = new RSACryptoServiceProvider(cspParams);
		   var sourceBytes = new UnicodeEncoding().GetBytes(data);

	   	var validSignature = algo.VerifyData(sourceBytes, "SHA256", _digitalSignature);

	   	MessageBox.Show(validSignature.ToString());
	   }

	   private void mnuEncryptConnectionString_Click(object sender, RoutedEventArgs e)
	   {
	   	var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
	   	var section = (ConnectionStringsSection)config.GetSection("connectionStrings");
		 section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
	   	section.SectionInformation.ForceSave = true;
		   config.Save(ConfigurationSaveMode.Full);

	   	MessageBox.Show(String.Format("ConnectionStrings encrypted on file {0}", section.CurrentConfiguration.FilePath));
	   }
   }
}
